package com.mars.sso.client.filter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.mars.base.common.utils.CookieUtils;
import com.mars.base.common.utils.web.WebUtil;

/**
 * 登录验证状态拦截器
 * 
 * @author yhaoquan
 * 
 */
public class SSOFilter implements Filter {

	Logger logger = LoggerFactory.getLogger(this.getClass());

	// 不需要拦截的URI模式，以正则表达式表示
	private String excludes;
	// 服务器公网访问地址
	private String serverBaseUrl;
	// 业务系统与SSO系统间通信的内网地址
	private String serverInnerAddress;
	// 登录页面URL地址
	private String loginPageAddress;
	// cookie的name
	private String SSO_VT = "vt";
	// 跨域运行访问的URL
	private String corsAllowOrigin;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.excludes = filterConfig.getInitParameter("excludes");
		this.serverBaseUrl = filterConfig.getInitParameter("serverBaseUrl");
		this.serverInnerAddress = filterConfig.getInitParameter("serverInnerAddress");
		this.loginPageAddress = filterConfig.getInitParameter("loginPageAddress");
		this.corsAllowOrigin = filterConfig.getInitParameter("corsAllowOrigin");

		if (serverBaseUrl == null || serverInnerAddress == null) {
			// throw new ServletException("SSOFilter配置错误，必须配置serverBaseUrl和serverInnerAddress") ;
		}

		TokenManager.serverInnerAddress = serverInnerAddress;
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.setContentType("application/json;charset=utf-8");

		// 如果不需要拦截的请，直接通过
		if (requestIsExclude(request)) {
			chain.doFilter(request, response);
			return;
		}

		logger.debug("进入SSOFilter，当前请求URL:{}", request.getRequestURL());

		// 进行登录状态验证
		String vt = CookieUtils.getCookieValue(request, SSO_VT);
		if (null != vt && !StringUtils.isEmpty(vt)) {
			SSOUser user = null;

			try {
				user = TokenManager.validate(vt);
			} catch (Exception e) {
				e.printStackTrace();
			}

			if (user != null) {
				holdUser(user, request); // 将user存放，供业务系统使用

				Date timeout = TokenManager.timeout(vt, Config.TIMEOUT);
				if (null == timeout) {
					loginCheck(request, response);
				} else {
					TokenManager.resetCookieTime(request, response, SSO_VT, vt, Config.TIMEOUT);
				}

				chain.doFilter(request, response); // 请求继续向下执行
			} else {
				// 删除无效的Token
				CookieUtils.deleteCookie(request, response, SSO_VT);
				// 引导浏览器重定向到服务器执行登录校验
				loginCheck(request, response);
			}

		} else {
			String vtParam = parseVtParam(request);
			if (vtParam == null) {
				// 请求中没有vtParam，引导浏览器重定向到服务器端执行登录校验
				loginCheck(request, response);
			} else if (vtParam.length() == 0) {
				// 有vtParam，但内容为空，表示到服务器端loginCheck后，得到的结果是未登录
				response.sendError(403);
			} else {
				// 让浏览器想本链接发起一次重定向，此过程去除vtParam，将vt写入cookie
				redirectToSelf(vtParam, request, response);
			}
		}

		// chain.doFilter(request, response);
	}

	// 引导浏览器重定向到服务器执行登录校验
	private void loginCheck(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// 返回JSON数据有客户端实现重定向执行

		// 将所有请求参数重新拼接成queryString
		String qstr = makeQueryString(request);
		// 回调地址
		String backUrl = request.getRequestURL() + qstr;
		String location = loginPageAddress + "?backUrl=" + backUrl;

		response.setHeader("Access-Control-Allow-Origin", this.corsAllowOrigin);
		PrintWriter out = null;
		Map<String, Object> result = new HashMap<String, Object>();
		try {
			out = response.getWriter();
			response.setStatus(HttpStatus.FORBIDDEN.value());
			result.put("status", response.getStatus());
			result.put("type", "noLogin");
			result.put("message", "未登录，请登录！");
			result.put("redirctBackUrl", location);
			out.print(JSON.toJSONString(result));

		} catch (IOException ioEx) {
			ioEx.printStackTrace();
		} finally {
			out.flush();
			out.close();
		}

		/*
		 * if ("XMLHttpRequest".equals(request.getHeader("x-request-with"))) { // 400状态表示请求格式错误，服务器没有理解请求，此处返回400状态表示未登录时服务器角色 response.sendError(400); } else {
		 * 
		 * // 将所有请求参数重新拼接成queryString String qstr = makeQueryString(request); // 回调地址 String backUrl = request.getRequestURL() + qstr; String location = loginPageAddress + "?backUrl=" + backUrl;
		 * 
		 * response.sendRedirect(location); }
		 */
	}

	// 从参数中获取服务器端传来的vt后，执行一个到本链接的重定向，将vt写入cookie
	// 重定向后再发来的请求就存在有效vt参数了
	private void redirectToSelf(String vt, HttpServletRequest request, HttpServletResponse response) throws IOException {
		final String PARAMNAME = "__vt_param__=";
		// 此处拼接redirect的url，去除vt参数部分
		StringBuffer location = request.getRequestURL();
		String qstr = request.getQueryString();

		int index = qstr.indexOf(PARAMNAME);
		if (index > -1) { // 还有其他参数，param1=param1&__vt_param__=xxx
			qstr = "?" + qstr.substring(0, qstr.indexOf(PARAMNAME) - 1);
		} else { // 没有其他参数 qstr = __vt_param__=xxx
			qstr = "";
		}

		location.append(qstr);

		// 将cookie写入到客户端
		CookieUtils.setCookie(request, response, SSO_VT, vt);

		response.sendRedirect(location.toString());
	}

	// 将所有请求参数重新拼接成queryString
	private String makeQueryString(HttpServletRequest request) {
		StringBuffer builder = new StringBuffer();
		Enumeration<String> parameterNames = request.getParameterNames();
		while (parameterNames.hasMoreElements()) {
			String paramName = (String) parameterNames.nextElement();
			String[] parameterValues = request.getParameterValues(paramName);
			for (String paramVals : parameterValues) {
				builder.append("&").append(paramName).append("=").append(WebUtil.encode(paramVals));
			}
		}
		if (builder.length() > 0) {
			builder.replace(0, 1, "?");
		}

		return builder.toString();
	}

	// 从请求参数中解析vt
	private String parseVtParam(HttpServletRequest request) {
		final String PARAMNAME = "__vt_param__=";
		String qstr = request.getQueryString();

		if (qstr == null) {
			return null;
		}

		int index = qstr.indexOf(PARAMNAME);
		if (index > -1) {
			return qstr.substring(index + PARAMNAME.length());
		} else {
			return null;
		}
	}

	// 将User存入threadLocal和request中，供业务系统使用
	private void holdUser(SSOUser user, ServletRequest request) {
		UserHolder.setUser(user, request);
	}

	/**
	 * 判断请求是否不需要拦截<br>
	 * 如不需要拦截的请求，在Filter中配置如下正则表达式 ^/((about$)|(contact$)|(notmember/.+))
	 * 
	 * @param request
	 * @return
	 */
	private boolean requestIsExclude(HttpServletRequest request) {
		// 没有设定excludes是，所有经过filter的请求都需要被处理
		if (StringUtils.isEmpty(excludes)) {
			return false;
		}

		// 获取去除context path后的请求路径
		String contextPath = request.getContextPath();
		String uri = request.getRequestURI();
		uri = uri.substring(contextPath.length());

		Pattern p = Pattern.compile(excludes);
		Matcher m = p.matcher(uri);
		// 正则表达式匹配URI被排除，不需要拦截
		boolean isExcluded = m.find();
		if (isExcluded) {
			logger.debug("request path: {} is excluded!", uri);
		}

		return isExcluded;
	}

	@Override
	public void destroy() {
	}

}
